/* eslint-disable no-shadow */
/* eslint-disable no-use-before-define */
/* eslint-disable camelcase */
/* eslint-disable consistent-return */

import { I_return } from '@/obj/ifs';
import pako from 'pako';
import Vue from 'vue';
import { ColumnConfig } from 'vxe-table/types/column';
import XEUtils from 'xe-utils';
import store from '../../store';

/**
 * @description: 将传入对象转换为数据
 *               1、排除对象属性名为_XID的属性
 *               2、排除对象属性名包含| 和 /
 * @param {object} obj 传入对象
 * @return {Array<{name:string ; value:any}>}
 * @author: wiz
 * @Date: 2023-07-14 12:32:12
 * @LastEditors: wiz
 * @LastEditTime: Do not edit
 */
function getObjNameValueToArr(obj: {
  [key: string]: any;
}): Array<{ name: string; value: any }> {
  const arr: any = [];
  for (const p in obj) {
    if (p !== '_XID' && p.indexOf('|') < 0 && p.indexOf('/') < 0) {
      arr.push({
        name: p,
        value: obj[p]
      });
    }
  }
  return arr;
}

/**
 * @description:将传入的Map转为数据
 * @param {Map<string, any>} m
 * @param {*} any
 * @return {*} Array<obj> | []
 * @author: wiz
 * @Date: 2023-07-14 20:46:45
 * @LastEditors: clr
 * @LastEditTime: Do not edit
 */

function mapToArr(m: Map<string, any>): Array<any> {
  const arr: Array<any> = [];
  m.forEach((v, k) => {
    const obj: Object = {};
    obj[k] = v;
    arr.push(obj);
  });
  return arr;
}

/**
 * @description: 传入obj转换为数据
 * @return {*}
 * @author: wiz
 * @Date: 2023-07-14 21:09:50
 * @LastEditors: clr
 * @LastEditTime: Do not edit
 */
function objToArr(
  obj: { [key: string]: any },
  key: string = 'key',
  value: string = 'value',
  options?: {
    ignore?: string[];
    ignoreRegex?: string[];
  }
): Array<Object> {
  const arr: any = [];
  const ignore = options ? options.ignore || [] : [];
  const ignoreRegex = options ? options.ignoreRegex || [] : [];
  for (const k in obj) {
    if (
      !XEUtils.find(ignoreRegex, el => k.indexOf(el) > -1) &&
      !XEUtils.find(ignore, el => el === k)
    ) {
      const o = {};
      o[key] = k;
      o[value] = obj[k];
      arr.push(o);
    }
  }
  return arr;
}

// // 获取值传出字符串
// /**
//  * @description:
//  * @param {any} value
//  * @param {string} format
//  * @return {*}
//  * @author: wiz
//  * @Date: 2023-07-14 21:35:48
//  * @LastEditors: clr
//  * @LastEditTime: Do not edit
//  */
// function getTime(value: any, format: string): string {
//   const date = new Date(value);
//   let ret: RegExpExecArray;
//   const opt = {
//     'y+': date.getFullYear().toString(), // 年
//     'M+': (date.getMonth() + 1).toString(), // 月
//     'd+': date.getDate().toString(), // 日
//     'H+': date.getHours().toString(), // 时
//     'm+': date.getMinutes().toString(), // 分
//     's+': date.getSeconds().toString(), // 秒
//     'S+': date.getMilliseconds().toString() // 毫秒
//     // 有其他格式化字符需求可以继续添加，必须转化成字符串
//   };
//   for (const k in opt) {
//     ret = new RegExp(`(${k})`).exec(format);
//     if (ret) {
//       format = format.replace(
//         ret[1],
//         ret[1].length === 1 ? opt[k] : opt[k].padStart(ret[1].length, '0')
//       );
//     }
//   }
//   return format;
// }

/**
 * @description: 将传入的数据利用base64编码加密成新的字符串
 * @param {string} str
 * @return {*}
 * @author: wiz
 * @Date: 2023-07-14 21:56:05
 * @LastEditors: clr
 * @LastEditTime: Do not edit
 */
function EnCode(str: string): I_return<string> {
  return wEnCode(str);
}

/**
 * @description: 将传入数据通过base64解码
 * @param {string} str
 * @return {*}
 * @author: wiz
 * @Date: 2023-07-14 22:18:41
 * @LastEditors: clr
 * @LastEditTime: Do not edit
 */
function DeCode(str: string): I_return<string> {
  return wDeCode(str);
}

/**
 * @Author: wiz
 * @param {boolean} delimiter 是否要分隔符 默认true
 * @return {*}
 * @description: guid
 */
function getGUID(delimiter: boolean = true): string {
  let L_template: string;
  if (delimiter) {
    L_template = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx';
  } else {
    L_template = 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx';
  }
  return L_template.replace(/[xy]/g, c => {
    // tslint:disable-next-line:no-bitwise
    const r: number = (Math.random() * 16) | 0;
    // tslint:disable-next-line:no-bitwise
    const v: number = c === 'x' ? r : (r & 0x3) | 0x8;
    return v.toString(16);
  });
}
/*
 @deprecated:2.0版本过滤条件是mongo格式
 */
function valueToFilter(type: string, condition: string, value: any): any {
  const res = {
    condition: null,
    value: null,
    filterValue: null
  };
  // 此处是处理通配条件，现在改用mongo,如果经此统一修改
  if (value) {
    if (type === 'date' || type === 'datetime' || type === 'time') {
      let newValue = null;
      newValue = [];
      if (condition === '>') {
        newValue[0] = value;
        if (type === 'time') {
          newValue[1] = '23:59:59';
        } else {
          const arr = value.split('-');
          arr[0] = '2050';
          newValue[1] = arr.join('-');
        }
      } else if (condition === '<') {
        newValue[1] = value;
        if (type === 'time') {
          newValue[0] = '00:00:01';
        } else {
          const arr = value.split('-');
          arr[0] = '1970';
          newValue[0] = arr.join('-');
        }
      } else if (condition === '=') {
        newValue[0] = value;
        newValue[1] = value;
      } else {
        newValue = value;
      }
      res.filterValue = newValue;
    } else if (type === 'number') {
      if (condition === ':') {
        res.filterValue = typeof value === 'object' ? value.join(':') : value;
        if (typeof value !== 'object') value = value.split(':');
      } else if (condition === '>' || condition === '<') {
        res.filterValue = condition + value;
      } else {
        res.filterValue = value;
      }
    } else if (type === 'string') {
      if (condition === 'in') {
        res.filterValue = `*${value}*`;
      } else if (condition === '=' || condition === '!=') {
        res.filterValue = condition + value;
      } else {
        res.filterValue = value;
      }
    }
    if (type === 'date' || type === 'datetime') {
      if (typeof value === 'object') {
        if (value[0].indexOf('.') > 0) {
          value[0] = value[0].split('.')[0];
        }
        if (value[1].indexOf('.') > 0) {
          value[1] = value[1].split('.')[0];
        }
      } else {
        value = value.split('.')[0];
      }
    }
  }
  res.value = value;
  res.condition = condition;
  return res;
}

// 横线转驼峰写法
/**
 * @description: 横线转驼峰写法
 * @param {string} name
 * @return {*}
 * @author: wiz
 * @Date: 2023-07-14 22:31:22
 * @LastEditors: clr
 * @LastEditTime: Do not edit
 */
function toHump(name: string): string {
  return name.replace(/-(\w)/g, function f(all, letter) {
    return letter.toUpperCase();
  });
}

// 驼峰转换横线
/**
 * @description: 驼峰转换横线
 * @param {string} name
 * @return {*}
 * @author: wiz
 * @Date: 2023-07-14 22:34:32
 * @LastEditors: clr
 * @LastEditTime: Do not edit
 */
function toLine(name: string): string {
  return name.replace(/([A-Z])/g, '-$1').toLowerCase();
}

/**
 * @description: findComponent转换组件名‘含-变成oname,不含-的就成o-name
 * @param {string} name
 * @return {*}
 * @author: wiz
 * @Date: 2023-07-13 21:10:39
 * @LastEditors: clr
 * @LastEditTime: Do not edit
 */
function findComponent(name: string): string {
  name = toLine(name);
  const f = name.substring(0, 1);
  if (f === '-') {
    return `o${name}`;
  }
  if (f === 'o' || f === 'c') {
    return name;
  }
  if (name.indexOf('-') > -1) {
    return `o-${name}`;
  }
  return `o${name}`;
}

/**
 * @description: 判断传入的值是否为 null|undefined，""|"   "，[]，
 * @param {any} val
 * @return {boolean}
 * @author: clr
 * @Date: 2023-07-02 16:02:06
 * @LastEditors: clr
 * @LastEditTime: Do not edit
 */
function noValue(val: any): boolean {
  if (val === null || val === undefined) {
    return true;
  }
  if (typeof val === 'string') {
    return val.replace(/(\s*)/g, ``) === ``;
  }
  if (typeof val === 'number') {
    return isNaN(val);
  }
  if (typeof val === 'object') {
    if (val instanceof Set) {
      return val.size === 0;
    }
    if (val instanceof Map) {
      return val.size === 0;
    }
    if (Array.isArray(val)) {
      return val.length === 0;
    }
    return Object.keys(val).length === 0 && val.constructor === Object;
  }

  return false;
}

/**
 * @description: 判断传入的值是否为 string
 * @param {string} str
 * @return {boolean}
 * @author: clr
 * @Date: 2023-07-02 16:02:06
 * @LastEditors: clr
 * @LastEditTime: Do not edit
 */
function isJSONString(str: string): boolean {
  if (typeof str === 'string') {
    try {
      JSON.parse(str);
      return true;
    } catch (e) {
      return false;
    }
  }
  return false;
}

/**
 * @description: 切分m2o的字符串
 *               传入值必须是包含3个'|'
 * @param {string} field - 字符串
 * @return {{ type: string; field: string; toTable: string; toField: string }|undefined}
 * @author: clr
 * @Date: 2023-07-02 16:02:06
 * @LastEditors: clr
 * @LastEditTime: Do not edit
 */
function splitM2OField(
  field: string
):
  | { type: string; field: string; toTable: string; toField: string }
  | undefined {
  const arr = field.split('|');
  if (arr.length !== 4) {
    return undefined;
  }
  return {
    type: arr[0],
    field: arr[1],
    toTable: arr[2],
    toField: arr[3]
  };
}

/**
 * @description:传入字符串或数组类型，并根据val的类型返回不同的配置对象
 * @param {string} 根据 val中的 ：切分传出toTable: m2o[0],toField: m2o[1],fields: [],isDropdown: false
 * @param {object} 如果传入的是object，传出object
 * @return {*}
 * @author: wiz
 * @Date: 2023-07-13 20:48:27
 * @LastEditors: clr
 * @LastEditTime: Do not edit
 */
function getM2OConfig(val: string | object): any {
  if (typeof val === 'string') {
    const m2o = val.split(':');
    if (m2o.length < 2) {
      return null;
    }
    return {
      toTable: m2o[0],
      toField: m2o[1],
      fields: [],
      isDropdown: false
    };
  }
  if (typeof val === 'object') {
    return val;
  }
}

/**
 * @description: 遇到 ‘|’切分成，返回type，field，selectionID
 *               如果传入值的切分值长度小于3组，返回NUll。
 * @param {string} field
 * @return {object}
 * @author: wiz
 * @Date: 2023-07-13 20:52:38
 * @LastEditors: clr
 * @LastEditTime: Do not edit
 */
function splitSelectionField(
  field: string
): { type: string; field: string; selectionID: string } | null {
  if (noValue(field) || typeof field !== 'string') {
    return null;
  }
  const arr = field.split('|');
  if (arr.length < 3) {
    return null;
  }
  return {
    type: arr[0],
    field: arr[1],
    selectionID: arr[2]
  };
}

/**
 * @description: joinTF 链接table和field
 * @param {string} table 表名
 * @param {string} field 字段名
 * @return {*}
 * @author: wiz
 * @Date: 2023-07-13 21:09:10
 * @LastEditors: clr
 * @LastEditTime: Do not edit
 */
function joinTF(table: string, field: string): string {
  return `${table}.${field}`;
}

// 递归权限
function configRecurse(config: any, data: any, property: string): any {
  if (config.type === 'layout') {
    this.configRecurse(config.config.first, data, property);
    this.configRecurse(config.config.second, data, property);
  } else {
    if (config.name === 'BaseTab') {
      const panels = config.config.panels;
      Object.keys(data).forEach(value => {
        const find = XEUtils.find(
          panels,
          el => el.content.config.name === value
        );
        if (find) {
          find.content.config[property] = data[value];
        } else {
          panels.forEach(panel => {
            if (panel.content?.config?.first) {
              this.configRecurse(
                panel.content.config.first.config,
                data,
                property
              );
              this.configRecurse(
                panel.content.config.second.config,
                data,
                property
              );
            }
          });
        }
      });
    } else if (
      config.name === 'BasePanel' ||
      config.name === 'CloneBasePanel'
    ) {
      const panels = config.config.tablePanels;
      Object.keys(data).forEach(value => {
        if (config.config.name === value) {
          config.config[property] = data[value];
        } else {
          const find = XEUtils.find(panels, el => el.label === value);
          if (find) {
            find.content[property] = data[value];
          }
        }
      });
    } else {
      Object.keys(data).forEach(value => {
        if (config.config.name === value) {
          config.config[property] = data[value];
        }
      });
    }
  }
  return config;
}

function configRecurseSingle(config: any, data: any, type: string): any {
  if (config.type === 'layout') {
    this.configRecurseSingle(config.config.first, data, type);
    this.configRecurseSingle(config.config.second, data, type);
  } else {
    if (config.name === 'BaseTab') {
      const panels: any = config.config.panels;
      panels.forEach(el => {
        if (el.content?.config?.first) {
          this.configRecurseSingle(el.content.config.first.config, data, type);
          this.configRecurseSingle(el.content.config.second.config, data, type);
        } else {
          el.content.config[type] = data;
        }
      });
    } else if (config.name === 'BasePanel') {
      const panels: any = config.config.tablePanels;
      config.config[type] = data;
      panels.forEach(el => {
        el.content[type] = data;
      });
    } else {
      config.config[type] = data;
    }
  }
  return config;
}

function getM2OConfigOption(cfg: any, row: any): any {
  // 是否为空 val=0 也判断为空
  function isEmpty(val: string | number): boolean {
    if (typeof val === 'string') {
      return val === '';
    }
    if (typeof val === 'number') {
      return val === 0;
    }
    return true;
  }
  const newCfg = XEUtils.clone(cfg, true);
  const pattern = new RegExp(/\$\(([^)]*)\)/);
  newCfg.forEach(el => {
    if (pattern.test(el.filter)) {
      // find target field
      let field = el.filter.match(pattern)[1];
      if (field.indexOf('.') > -1) {
        const arr = field.split('.');
        if (arr[1] === 'm2o') {
          const keys = Object.keys(row);
          for (let i = 0; i < keys.length; i++) {
            if (keys[i].indexOf(`m2o|${arr[0]}`) === 0) {
              field = keys[i];
              break;
            }
          }
        }
      }
      el.filter = isEmpty(row[field]) ? '' : row[field];
    }
  });
  return newCfg;
}

/**
 * @description: 用于创建一个弹窗（模态框）并返回一个Promise对象
 * @param {string} name 弹窗名
 * @param {any} config  弹窗配置
 * @return {*}
 * @author: wiz
 * @Date: 2023-07-22 12:19:34
 * @LastEditors: clr
 * @LastEditTime: Do not edit
 */
function createModal(
  name: string,
  config: any,
  custEventName?: string
): Promise<any> {
  // 将返回结果通过resolve函数传递给Promise，从而完成Promise异步操作。
  return new Promise((resolve, reject) => {
    // 调用store.dispatch方法增加一个弹窗计数，获取当前弹窗计数值，并赋给id变量
    store.dispatch('addModalCount');
    const id: number = store.state.App.modalCount;
    const sig = `modal_${id}`;
    // 检查有人监听
    // 创建一个唯一标识sig,通过Vue.prototype.$bus的emit方法来触发addModal自定义事件，传入弹窗的名称、配置和id参数。
    Vue.prototype.$bus.$emit('addModal', name, config, id);
    // 等待弹窗返回结果，一旦接收结果，触发removeModal事件移除弹窗，并通过off方法解绑sig事件
    Vue.prototype.$bus.$on(sig, (data: unknown) => {
      Vue.prototype.$bus.$emit('removeModal', id);
      Vue.prototype.$bus.$off(sig);
      resolve(data);
    });
  });
}

// 判断是否包含中文clr
/**
 * @description: 判断是否包含中文
 * @param {*} str
 * @return {*}
 * @author: wiz
 * @Date: 2023-07-19 22:09:09
 * @LastEditors: clr
 * @LastEditTime: Do not edit
 */
function isContainChinese(str) {
  const reg = /[\u4E00-\u9FA5\uF900-\uFA2D]/;
  return reg.test(str);
}

/**
 * @description: 获取文件扩展名
 * @param {string} fileSrc 文件名
 * @return {*}
 * @author: wiz
 * @Date: 2023-07-19 22:10:53
 * @LastEditors: clr
 * @LastEditTime: Do not edit
 */
const getFileExt = (fileSrc: string): string => {
  let fileExt: string = '';
  let fileName: string = fileSrc;
  // 判断是否包含/？有则切分数组src_arr，然后将数组中的最后一个元素作为文件名赋给变量fileName
  if (fileSrc.indexOf('/') > -1) {
    const src_arr = fileSrc.split('/');
    fileName = src_arr[src_arr.length - 1];
  }
  // 判断是否包含.？是则将fileName分割成数组arr，然后将arr中的最后一个元素作为扩展名赋给变量fileExt
  if (fileName.indexOf('.') > -1) {
    const arr = fileName.split('.');
    fileExt = arr[arr.length - 1];
  }
  // 最后，函数返回变量fileExt，即文件的扩展名。
  return fileExt;
};

/**
 * @description: 获取文件名包含扩展名
 * @param {string} fileSrc 文件名
 * @return {*}
 * @author: wiz
 * @Date: 2023-07-19 22:11:52
 * @LastEditors: clr
 * @LastEditTime: Do not edit
 */
const getFileName = (fileSrc: string): string => {
  let fileName = fileSrc;
  if (fileSrc.indexOf('/') > -1) {
    const src_arr = fileSrc.split('/');
    fileName = src_arr[src_arr.length - 1];
  }
  if (fileName.indexOf('.') > -1) {
    const arr = fileName.split('.');
    fileName = arr[0];
  }
  return fileName;
};

/**
 * @description: 获取文件类型
 * @param {string} fileSrc 文件名
 * @return {*}
 * @author: wiz
 * @Date: 2023-07-19 22:12:44
 * @LastEditors: clr
 * @LastEditTime: Do not edit
 */
const getFileType = (fileSrc: string): string => {
  let type: string = 'unknown';
  let fileName: string = fileSrc;
  if (fileSrc) {
    if (fileSrc.indexOf('/') > -1) {
      const src_arr = fileSrc.split('/');
      fileName = src_arr[src_arr.length - 1];
    }
    if (fileName.indexOf('.') > -1) {
      const arr = fileName.split('.');
      const ext = arr[arr.length - 1];
      const exts = {
        image: ['png', 'jpg', 'jpeg', 'bmp', 'gif', 'webp'],
        word: ['doc', 'docx'],
        excel: ['xls', 'xlsx'],
        pdf: ['pdf'],
        ppt: ['ppt', 'pptx'],
        txt: ['txt'],
        video: ['mp4', 'm2v', 'mkv', 'rmvb', 'wmv', 'avi', 'flv', 'mov', 'm4v'],
        audio: ['mp3', 'wav', 'wmv', 'flac'],
        cad: ['dwg', 'dxf', 'dwt', 'dwf', 'dwl', 'dwl2'],
        exe: ['exe'],
        ios: ['ios'],
        psd: ['psd'],
        web: ['html', 'css', 'js'],
        zip: ['rar', '7z', 'zip']
      };
      for (const key in exts) {
        if (exts[key].indexOf(ext) > -1) {
          type = key;
          break;
        }
      }
    }
  }
  return type;
};

/**
 * @description: 获取文件的路径
 * @param {string} id  文件id
 * @param {boolean} isThumb 是否缩略图
 * @return {*}
 * @author: wiz
 * @Date: 2023-07-19 22:13:32
 * @LastEditors: clr
 * @LastEditTime: Do not edit
 */
// const getFileSrc = (id: string, isThumb: boolean = true): string => {
//   return `${lexicon.url.loadFile}?userNo=${store.getters.UserNo}&&token=${store.getters.Token}&&params=%7b%22fileId%22:${id},%22isThumbnail%22:${isThumb}%7d`;
// };

/**
 * @description:将传入的dataual值转换为blob对象
 * @param {string} dataurl
 * @return {*}
 * @author: wiz
 * @Date: 2023-07-19 22:15:37
 * @LastEditors: clr
 * @LastEditTime: Do not edit
 */
const dataURLtoBlob = (dataurl: string): any => {
  const arr = dataurl.split(',');
  const mime = arr[0].match(/:(.*?);/)[1];
  const bstr = atob(arr[1]);

  let n: number = bstr.length;
  const u8arr: any = new Uint8Array(n);
  while (n--) {
    u8arr[n] = bstr.charCodeAt(n);
  }
  return new Blob([u8arr], { type: mime });
};

/**
 * @description: 下载文件
 * @param {string} url
 * @param {string} name
 * @return {*}
 * @author: wiz
 * @Date: 2023-07-19 22:19:40
 * @LastEditors: clr
 * @LastEditTime: Do not edit
 */
const downloadFile = (url: string, name: string): void => {
  const a = document.createElement('a');
  a.setAttribute('href', url);
  a.setAttribute('download', name);
  const clickEvent = document.createEvent('MouseEvents');
  clickEvent.initEvent('click', true, true);
  a.dispatchEvent(clickEvent);
};

/**
 * @description: 下载文件（按Base64）
 * @param {string} base64
 * @param {string} name
 * @return {*}
 * @author: wiz
 * @Date: 2023-07-19 22:20:40
 * @LastEditors: clr
 * @LastEditTime: Do not edit
 */
const downloadFileByBase64 = (base64: string, name: string): void => {
  const myBlob = dataURLtoBlob(base64);
  const myUrl = URL.createObjectURL(myBlob);
  downloadFile(myUrl, name);
};

/**
 * @description: 获取列类型
 * @param {ColumnConfig} column
 * @return {*}
 * @author: wiz
 * @Date: 2023-07-19 22:23:14
 * @LastEditors: clr
 * @LastEditTime: Do not edit
 */
const getColumnType = (column: ColumnConfig): any => {
  const res = {
    name: null,
    type: 'string',
    renderType: 'string'
  };
  if (column.editRender && column.editRender.name) {
    res.name = column.editRender.name;
    if (column.editRender.name === 'oDatePicker') {
      const type = column.editRender.props.type;
      res.type = type;
      res.renderType = type;
    } else if (column.editRender.name === 'oNumberInput') {
      res.type = 'number';
      res.renderType = 'number';
    }
  }
  return res;
};

/**
 * @description: 获取url参数值
 * @param {string} name 参数名
 * @return {*}
 * @author: wiz
 * @Date: 2023-07-19 22:26:45
 * @LastEditors: clr
 * @LastEditTime: Do not edit
 */
const getURLParam = (name: string): any => {
  return (
    decodeURIComponent(
      // eslint-disable-next-line no-useless-concat
      (new RegExp(`[?|&]${name}=` + `([^&;]+?)(&|#|;|$)`, 'ig').exec(
        location.search
      ) || [''])[1].replace(/\+/g, '%20')
    ) || null
  );
};

/**
 * @description: 获取一年第一周的天数
 * @param {number} year 四位数字年
 * @param {number} mode 模式3|6
 * * mode 6 周日为星期一, mode 3 周 1 为星期一
 * 参考mysql YEARWEEK('2016-12-31',3)模板用法与数据库一致
 * mode只接受 3|6 如果不是3或6 则按3处理
 * @return {*} 天数
 * @author: wiz
 * @Date: 2023-07-19 22:29:36
 * @LastEditors: clr
 * @LastEditTime: Do not edit
 */
const GetYearFirstWeekDays = (year: number, mode: number): number => {
  const t = new Date();
  const yearFirstDay = new Date(t.setFullYear(year, 0, 1));
  // 本年第一天星期几
  const firstDayInWeek = yearFirstDay.getDay();
  // 年第一周天数
  let firstWeekDays = 1;
  if (firstDayInWeek !== 0) {
    firstWeekDays = 7 - firstDayInWeek + 1;
    if (mode === 6) {
      --firstWeekDays;
    }
  } else {
    if (mode === 6) {
      // 周日算一周起始 反之为一天
      firstWeekDays = 7;
    }
  }
  return firstWeekDays;
};
/**
 * @description: 获取一年最后一周的天数
 * @param year 四位数字年
 * @param mode  模式 3|6
 * mode 6 周日为星期一, mode 3 周 1 为星期一
 * 参考mysql YEARWEEK('2016-12-31',3)模板用法与数据库一致
 * mode只接受 3|6 如果不是3或6 则按3处理
 * @returns 天数
 */
const GetYearLastWeekDays = (year: number, mode: number): number => {
  const t = new Date();
  const yearLastDay = new Date(t.setFullYear(year, 11, 31));
  // 本年最后一天星期几
  const lastDayInWeek = yearLastDay.getDay();
  // 年最后一周天数
  let lastWeekDays = 7;
  if (lastDayInWeek !== 0) {
    lastWeekDays = lastDayInWeek;
    if (mode === 6) {
      lastWeekDays++;
    }
  } else {
    if (mode === 6) {
      lastWeekDays = 1;
    }
  }
  return lastWeekDays;
};

/**
 * @description:得到一年总周数
 * @param year 四位数字年
 * @param mode  模式 3|6
 * mode 6 周日为星期一, mode 3 周 1 为星期一
 * 参考mysql YEARWEEK('2016-12-31',3)模板用法与数据库一致
 * mode只接受 3|6 如果不是3或6 则按3处理
 * @returns 周数
 */
const GetYearSumWeeks = (year: number, mode: number): number => {
  const t = new Date();
  const firstWeekDays = GetYearFirstWeekDays(year, mode);
  const lastWeekDays = GetYearLastWeekDays(year, mode);
  const yearLastDay: any = new Date(t.setFullYear(year, 11, 31));
  const yearFirstDay: any = new Date(t.setFullYear(year, 0, 1));
  const diff = yearLastDay - yearFirstDay;
  const yearDay = Math.ceil(diff / (24 * 60 * 60 * 1000));
  let i = 0;
  if (firstWeekDays >= 4) {
    i++;
  }
  if (lastWeekDays >= 4) {
    i++;
  }
  return Math.floor((yearDay - firstWeekDays - lastWeekDays) / 7 + i);
};

/**
 * @description: 传入日期和模板,得到当年周数
 * @param {any} date 日期
 * @param {number} mode 模式3|6
 * * mode 6 周日为星期一, mode 3 周 1 为星期一
 * 参考mysql YEARWEEK('2016-12-31',3)模板用法与数据库一致
 * mode只接受 3|6 如果不是3或6 则按3处理
 * @return {*}
 * @author: wiz
 * @Date: 2023-07-19 22:33:31
 * @LastEditors: clr
 * @LastEditTime: Do not edit
 */
const GetCurrWeekByDate = (date: any, mode: number): string => {
  // GetCurrWeekByDateNew(date, mode);
  const d1: any = new Date(date);
  const d2: any = new Date(date);
  d2.setMonth(0);
  d2.setDate(1);
  const rq = d1 - d2;
  // 本年第几天
  const yearDay = Math.ceil(rq / (24 * 60 * 60 * 1000));
  const firstWeekDays = GetYearFirstWeekDays(date.getFullYear(), mode);
  const lastWeekDays = GetYearLastWeekDays(date.getFullYear(), mode);

  let i = 1;
  // 如果第一周天数不足4天 则第一周算上年末周
  if (firstWeekDays < 4) {
    i = 0;
  }
  let ii = 0;
  // 如果本年最后一周有4或4天以上则最后一周算本年
  if (lastWeekDays >= 4) {
    ii++;
  }
  let week = 0;
  let year = date.getFullYear();
  if (yearDay <= firstWeekDays) {
    if (i === 0) {
      year--;
      week = GetYearSumWeeks(date.getFullYear(), mode);
    } else {
      week = 1;
    }
  } else {
    if (yearDay >= lastWeekDays) {
      // 年未周算来年
      if (ii === 0) {
        week = 1;
        year++;
      } else {
        week = Math.floor((yearDay - firstWeekDays) / 7 + i + ii);
      }
    } else {
      week = Math.floor((yearDay - firstWeekDays) / 7 + i);
    }
  }
  const w: string = week < 10 ? `0${week}` : `${week}`;
  return year + w;
};

/**
 * @Author: wiz
 * @param treeArr 树结构数组
 * callBack(item)处理回调 例如映射地图
 * keyName key名默认 id
 * children 下一层名 默认 children
 * parentName 父ID属性名 默认 parentID
 * @return {*}
 * @description:在树结构数组中加入id 和 parentID
 * 如果对象带keyName 则不处理,但callBack会执行
 */
const addTreeKeyValue = (
  treeArr: Array<object>,
  callBack: any,
  keyName: string = 'id',
  childrenName: string = 'children',
  parentName: string = 'parentID'
): void => {
  let G_id: number = 0;
  const Handle = (
    obj: any,
    callBack: any,
    parentID: number,
    keyName: string,
    parentName: string,
    childrenName: string
  ) => {
    const childArr: Array<Object> = obj[childrenName];
    if (childArr && childArr.length > 0) {
      for (const item of childArr) {
        const L_id = ++G_id;
        addELE(item, callBack, L_id, parentID, keyName, parentName);
        Handle(item, callBack, L_id, keyName, parentName, childrenName);
      }
    }
  };
  const addELE = (
    item: any,
    callBack: any,
    id: number,
    pID: number,
    keyName: string,
    parentName: string
  ): void => {
    // 如果对象带keyName 则不处理
    const L_key = Object.keys(item).find(k => k === keyName);
    if (!L_key) {
      item[keyName] = id;
      item[parentName] = pID;
    }
    callBack(item);
  };

  for (const item of treeArr) {
    const L_id: number = ++G_id;
    addELE(item, callBack, L_id, null, keyName, parentName);
    const childArr: Array<Object> = item[childrenName];
    if (childArr && childArr.length > 0) {
      Handle(item, callBack, L_id, keyName, parentName, childrenName);
    }
  }
};

/**
 * @Author: wiz
 * @param {*} source
 * @param {*} target
 * @return {*}
 * @description: 将对象source的相同属性值复制到target
 */
const listAssign = (source: object, target: object): void =>
  Object.keys(target).forEach(key => {
    target[key] = source[key] || target[key];
  });

/**
 * @Author: wiz
 * @param {*} obj
 * @return {*}
 * @description:所有属性为null
 */
const objAttrClear = (obj: object): any => {
  const L_obj = XEUtils.clone(obj);
  Object.keys(L_obj).forEach(key => {
    L_obj[key] = null;
  });
  return L_obj;
};
// 根据下面的星期 获取日期
/**
 * @description: 根据传入的星期获取日期
 *               创建一个新的Date对象，赋值给变量date。
 *               使用P_dayDate.getTime()获取P_dayDate的时间戳，并加上P_day天数对应的毫秒数（P_day * 24 * 60 * 60 * 1000）。
 *               使用date.setTime方法将新的时间戳设置给date对象，以更新日期。
 *               返回更新后的date对象作为函数的结果。
 * @param {Date} P_dayDate
 * @param {number} P_day
 * @return {*}
 * @author: wiz
 * @Date: 2023-07-19 22:35:57
 * @LastEditors: clr
 * @LastEditTime: Do not edit
 */
const getDateFromDay = (P_dayDate: Date, P_day: number): Date => {
  const date = new Date();
  date.setTime(P_dayDate.getTime() + P_day * 24 * 60 * 60 * 1000);
  return date;
};
// 获取周的开始
/**
 * @description: 根据给定日期获取一周的起始日期
 * @param {Date} P_date
 * @param {number} P_mode
 * @return {*}
 * @author: wiz
 * @Date: 2023-07-19 22:39:10
 * @LastEditors: clr
 * @LastEditTime: Do not edit
 */
const getWeekStartByDate = (P_date: Date, P_mode: number): Date => {
  let startIdx = 0;
  if (P_mode === 2 || P_mode === 3 || P_mode === 6 || P_mode === 7) {
    startIdx = 1;
  }
  const day = P_date.getDay();
  return getDateFromDay(
    P_date,
    -day + startIdx > 0 ? -(day || 7) + startIdx : -day + startIdx
  );
};

// GetCurrWeekByDate 的更新版
/**
 * @description: 根据给定日期获取一周的起始日期的更新版
 * @param {Date} P_date
 * @param {number} P_mode
 * @return {*}
 * @author: wiz
 * @Date: 2023-07-19 22:39:10
 * @LastEditors: clr
 * @LastEditTime: Do not edit
 */
const GetCurrWeekByDateNew = (P_date: Date, P_mode: number): Object => {
  const getWeekDay = (P_date: Date, P_number: number): Date => {
    const L_date = new Date(P_date);
    let L_weekDay = L_date.getDay();
    L_weekDay =
      L_weekDay === P_number ? 0 : L_weekDay ? L_weekDay - P_number : 6;
    L_date.setDate(L_date.getDate() - L_weekDay);
    return L_date;
  };

  const getFirstWeekIsThisYear = (P_date: Date, P_mode: number): boolean => {
    const weekStart = P_mode % 2;
    let weekSentenceRule = 'fourAndMore';
    if (P_mode === 5 || P_mode === 7) {
      weekSentenceRule = 'monday';
    }
    if (P_mode === 0 || P_mode === 2) {
      weekSentenceRule = 'sunday';
    }
    const L_date = new Date(P_date);
    const L_11date = new Date(L_date.getFullYear(), 0, 1);
    const L_11dateWeekDay = L_11date.getDay();
    let L_firstWeekIsThisYear = true;
    if (weekSentenceRule === 'monday') {
      L_firstWeekIsThisYear =
        getWeekDay(L_11date, 1).getFullYear() === L_11date.getFullYear();
    } else if (weekSentenceRule === 'sunday') {
      L_firstWeekIsThisYear =
        getWeekDay(L_11date, 0).getFullYear() === L_11date.getFullYear();
    } else {
      const L_minus = L_11dateWeekDay - weekStart;
      (L_minus >= 4 || L_minus < 0) && (L_firstWeekIsThisYear = false);
    }
    return L_firstWeekIsThisYear;
  };
  const getWeekInYear = (P_date: Date, P_mode: number): number => {
    // 判断该星期是否跨年，如果跨年就是第一周
    const weekStartDate = getWeekStartByDate(P_date, P_mode); // 一周开始时间
    const endDate = getDateFromDay(weekStartDate, 6); // 一周结束时间
    if (weekStartDate.getFullYear() !== endDate.getFullYear()) {
      const num = 1;
      return num;
    }
    const d1 = new Date(P_date);
    let d2 = new Date(P_date);
    d2.setMonth(0);
    d2.setDate(1);
    d2 = getWeekStartByDate(d2, P_mode);
    const rq = d1.getTime() - d2.getTime();
    const days = Math.ceil(rq / (24 * 60 * 60 * 1000)) + 1;
    const num = Math.ceil(days / 7);
    return num;
  };
  const weekStart = P_mode % 2;
  let startIdx = 0;
  if (P_mode === 2 || P_mode === 3 || P_mode === 6 || P_mode === 7) {
    startIdx = 1;
  }
  const dateasdf = new Date(P_date);
  let L_weeks = getWeekInYear(dateasdf, P_mode);
  let L_year = dateasdf.getFullYear();
  if (L_weeks === 1) {
    const L_dt = new Date(P_date);
    L_dt.setFullYear(L_dt.getFullYear() - 1);
    L_dt.setMonth(0);
    L_dt.setDate(1);
    const L_isty = getFirstWeekIsThisYear(L_dt, P_mode);
    const L_dt1 = new Date(P_date);
    L_dt1.setMonth(0);
    L_dt1.setDate(1);
    const L_dt2 = getWeekStartByDate(L_dt1, weekStart);
    L_dt2.setDate(L_dt2.getDate() - 1);
    const L_num = getWeekInYear(L_dt2, P_mode) + 1;
    let L_is = 0;
    if (L_isty) {
      L_is++;
    }
    if (!startIdx) {
      L_is--;
    }
    L_weeks = L_num - 1 + L_is;
    L_year--;
  } else {
    const L_firstWeekIsThisYear = getFirstWeekIsThisYear(P_date, P_mode);
    let L_i = 0;
    if (L_firstWeekIsThisYear) {
      L_i++;
    }
    if (!startIdx) {
      L_i--;
    }
    L_weeks = L_weeks - 1 + L_i;
  }
  return { weeks: L_weeks, year: L_year };
};

/**
 * @description: 产生UUID
 * @param {number} len 自定义长度UUID 默认0 标准UUID长度
 * @param {number} radix 进制 2 10 16 参数无效产生标准UUID
 * @param {string} range  'letters'(大小写字母)  | 'number'(数字) | 'lettersANDnumber' (字母+数字)
 *                        默认 'lettersANDnumber' (字母+数字)
 * @return {string} 返回字符串
 * @author: wiz
 * @Date: 2023-07-06 12:43:04
 * @LastEditors: wiz
 * @LastEditTime: Do not edit
 */
const custUUID = (
  len: number = 0,
  radix: number = 16,
  range: 'letters' | 'number' | 'lettersANDnumber' = 'lettersANDnumber'
): string => {
  let chars: Array<string> = [];
  if (range === 'letters') {
    chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');
  } else if (range === 'number') {
    chars = '0123456789'.split('');
  } else {
    chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split(
      ''
    );
  }

  const uuid = [];
  let i;
  radix = radix || chars.length;

  if (len) {
    // Compact form
    for (i = 0; i < len; i++) uuid[i] = chars[0 | (Math.random() * radix)];
  } else {
    // rfc4122, version 4 form
    let r;

    // rfc4122 requires these characters
    // eslint-disable-next-line no-multi-assign
    uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';
    uuid[14] = '4';

    // Fill in random data. At i==19 set the high bits of clock sequence as
    // per rfc4122, sec. 4.1.5
    for (i = 0; i < 36; i++) {
      if (!uuid[i]) {
        r = 0 | (Math.random() * 16);
        uuid[i] = chars[i === 19 ? (r & 0x3) | 0x8 : r];
      }
    }
  }

  return uuid.join('');
};

/**
 * @description: 定时执行
 * @param {number} intervalTime 间隔时间(毫秒)
 * @param {Functionn} callback  要执行的回调()=>boolean
 * @param {number} totalDuration  总时长 毫秒如果是 此参数必须是intervalTime的倍数
 * @param {boolean} interrupted  在总时长内只要执行一次成功就中断
 * @return {Promise<any>}
 * @author: wiz
 * @Date: 2023-07-06 12:43:04
 * @LastEditors: wiz
 * @LastEditTime: Do not edit
 */
const timedExec = async (
  intervalTime: number,
  callBack: () => boolean,
  totalDuration: number,
  interrupted: boolean = false
): Promise<any> => {
  const results = [];

  const total: number = Math.floor(totalDuration / intervalTime);

  if (totalDuration < intervalTime) {
    return Promise.all(results);
  }
  for (let i = 1; i <= total; i++) {
    results.push(
      new Promise((resolve, reject) =>
        setTimeout(() => {
          callBack() ? resolve(true) : reject(false);
        }, i * intervalTime)
      )
    );
  }
  if (interrupted) {
    return Promise.any(results);
  }
  return Promise.all(results);
};
/**
 * @description: 克隆不包括vue,vue对象只是引用关系
 *               对Set类型也有效
 * @param {any} o 对象
 * @param {boolean} deep 深复制 默认false
 * @return {any} 返回复制后的对象
 * @author: wiz
 * @Date: 2023-07-06 12:43:04
 * @LastEditors: wiz
 * @LastEditTime: Do not edit
 */
const cloneNoMatchVue = (o: any, deep: boolean = false): any => {
  const obj = {};
  Object.keys(o).forEach(key => {
    if (o[key]?._isVue || typeof o[key] !== 'object') {
      obj[key] = o[key];
    } else {
      if (o[key] instanceof Set) {
        obj[key] = new Set(o[key]);
      } else {
        obj[key] = XEUtils.clone(o[key], deep);
      }
    }
  });
  return obj;
};
/**
 * @description: 通过路径查找获取值
 * @param {any} obj
 * @param {string} path 'a.b.c'
 * @return {*} 未找到返回undefined
 * @author: wiz
 * @Date: 2023-07-06 12:43:04
 * @LastEditors: wiz
 * @LastEditTime: Do not edit
 */
function getObjValByPath(obj: any, path: string): any {
  const keys = path.split('.'); // 将路径拆分为键数组
  if (keys.length === 0) {
    return undefined;
  }
  // 递归函数，用于在对象中查找路径对应的值
  function findValue(obj: any, keys: string[]): any {
    if (keys.length === 0) {
      return obj; // 已经遍历完路径，返回最终值
    }

    const key = keys.shift(); // 取出第一个键
    if (obj && typeof obj === 'object' && key in obj) {
      return findValue(obj[key], keys); // 继续递归查找下一个键
    }

    return undefined; // 路径不存在，返回undefined
  }

  return findValue(obj, keys);
}
/**
 * @description: 通过路径设置对象值
 * @param {any} obj
 * @param {string} path
 * @param {any} value
 * @return {boolean}
 * @author: wiz
 * @Date: 2023-07-06 12:50:17
 * @LastEditors: wiz
 * @LastEditTime: Do not edit
 */
function setObjValByPath(obj: any, path: string, value: any): boolean {
  const keys = path.split('.'); // 将路径拆分为键数组
  // 递归函数，用于在对象中设置路径对应的值
  function setValue(obj: any, keys: string[], value: any): boolean {
    if (keys.length === 1) {
      // if (!obj[keys[0]]) {
      //   return false;
      // }
      obj[keys[0]] = value; // 已经遍历完路径，设置最终值
      return true;
    }

    const key = keys.shift(); // 取出第一个键
    if (obj && typeof obj === 'object' && key in obj) {
      return setValue(obj[key], keys, value); // 继续递归设置下一个键
    }
  }
  return setValue(obj, keys, value);
}
/**
 * @description: 通过值获取对象的路径
 *               返回第一次符合的路径
 * @param {any}obj 对象
 * @param {any}targetValue 目标值
 * @param {any}currentPath 当前路径默认''
 * @return {string | undefined}
 * @author: wiz
 * @Date: 2023-07-15 13:11:47
 * @LastEditors: wiz
 * @LastEditTime: Do not edit
 */
function findPathFirst(
  obj: any,
  targetValue: any,
  currentPath: string = ''
): string | undefined {
  for (const key in obj) {
    // eslint-disable-next-line no-prototype-builtins
    if (obj.hasOwnProperty(key)) {
      const value = obj[key];
      const newPath = currentPath ? `${currentPath}.${key}` : key;

      if (value === targetValue) {
        return newPath;
      }

      if (typeof value === 'object' && value !== null) {
        const foundPath = findPathFirst(value, targetValue, newPath);
        if (foundPath) {
          return foundPath;
        }
      }
    }
  }

  return undefined;
}
/**
 * @description: 通过值获取对象的路径
 * @param {any}obj 对象
 * @param {any}targetValue 目标值
 * @param {any}currentPath 当前路径默认''
 * @param {Array<string>}paths 存储查找到的路径
 * @return {Array<string>}
 * @author: wiz
 * @Date: 2023-07-15 13:11:47
 * @LastEditors: wiz
 * @LastEditTime: Do not edit
 */
function findPaths(
  obj: any,
  targetValue: any,
  currentPath: string = '',
  paths: string[] = []
): Array<string> {
  for (const key in obj) {
    // eslint-disable-next-line no-prototype-builtins
    if (obj.hasOwnProperty(key)) {
      const value = obj[key];
      const newPath = currentPath ? `${currentPath}.${key}` : key;

      if (value === targetValue) {
        paths.push(newPath);
      }

      if (typeof value === 'object' && value !== null) {
        findPaths(value, targetValue, newPath, paths);
      }
    }
  }

  return paths;
}

const CryptoJS = require('crypto-js');

/**
 * @description:获得md5哈希值
 * @param {string} p
 * @return {string} md5哈希值
 * @author: wiz
 * @Date: 2023-08-11 14:49:11
 * @LastEditors: swc
 * @LastEditTime: Do not edit
 */
function md5(p: string): string {
  return CryptoJS.MD5(p).toString();
}

/**
 * @description: AES加密
 * @param {string} data 数据
 * @param {string} key  盐
 * @return {string}
 * @author: wiz
 * @Date: 2023-08-11 14:57:46
 * @LastEditors: wiz
 * @LastEditTime: Do not edit
 */
function aesEncrypt(data: string, key: string): string {
  return CryptoJS.AES.encrypt(data, key).toString();
}
/**
 * @description: ASE解密
 * @param {string} data 数据
 * @param {string} key 盐
 * @return {*}
 * @author: wiz
 * @Date: 2023-08-11 14:59:38
 * @LastEditors: wiz
 * @LastEditTime: Do not edit
 */
function aesDecrypt(data: string, key: string): string {
  return CryptoJS.AES.decrypt(data, key).toString(CryptoJS.enc.Utf8);
}
/**
 * @description: DES加密
 * @param {string} data 数据
 * @param {string} key  盐
 * @return {string}
 * @author: wiz
 * @Date: 2023-08-11 14:57:46
 * @LastEditors: wiz
 * @LastEditTime: Do not edit
 */
function desEncrypt(data: string, key: string): string {
  return CryptoJS.DES.encrypt(data, key).toString();
}
/**
 * @description: DES解密
 * @param {string} data 数据
 * @param {string} key 盐
 * @return {*}
 * @author: wiz
 * @Date: 2023-08-11 14:59:38
 * @LastEditors: wiz
 * @LastEditTime: Do not edit
 */
function desDecrypt(data: string, key: string): string {
  return CryptoJS.DES.decrypt(data, key).toString(CryptoJS.enc.Utf8);
}
/**
 * @description: 返回串中的中文个数，字母和数据的个数
 * @param {string} str
 * @return {*}
 * @author: wiz
 * @Date: 2023-08-22 19:24:36
 * @LastEditors: wiz
 * @LastEditTime: Do not edit
 */
function countCharacters(str: string): { chinese: number; english: number } {
  let chineseCount = 0;
  let englishCount = 0;

  for (let i = 0; i < str.length; i++) {
    const char = str.charAt(i);
    // 使用 Unicode 编码范围来判断字符是否为中文字符
    if (/[\u4e00-\u9fa5]/.test(char)) {
      chineseCount++;
    } else if (/[a-zA-Z0-9]/.test(char)) {
      englishCount++;
    }
  }

  return { chinese: chineseCount, english: englishCount };
}
/*
比较两个对象的属性是否相等
如果 depth 大于 0，则继续递归并将 depth - 1 作为递归调用的参数。如果 depth 为 0，则不再进行递归调用，直接返回比较结果。
*/

/**
 * @description: 比较两个对象的属性是否相等
 * @param {string} obj1
 * @param {string} obj2
 * @param {number} depth 比较的深度层次控制，默认为0
 *                       如果 depth 为 <=0，则不再进行深度比较
 *                       如果传入带小数则取整数位处理
 * @return {boolean} true 相同 false 不同
 * @author: wiz
 * @Date: 2023-08-22 19:24:36
 * @LastEditors: wiz
 * @LastEditTime: Do not edit
 */
function compareObjectProperties(
  obj1: object,
  obj2: object,
  depth: number = 0
): boolean {
  // 修正 depth 参数
  depth = Math.floor(depth); // 取整数
  if (depth < 0) {
    depth = 0; // 小于零按 0理
  }

  // 检查数据类型
  if (
    typeof obj1 !== 'object' ||
    typeof obj2 !== 'object' ||
    obj1 === null ||
    obj2 === null
  ) {
    return false;
  }

  // 比较属性个数
  const keys1 = Object.keys(obj1);
  const keys2 = Object.keys(obj2);
  if (keys1.length !== keys2.length) {
    return false;
  }

  // 递归比较每个属性的值
  for (const key of keys1) {
    if (
      depth > 0 &&
      !compareObjectProperties(obj1[key], obj2[key], depth - 1)
    ) {
      return false;
    }
  }

  return true;
}
/**
 * @description: 定义小数的长度
 * @param {number} d 数据
 * @param {number} decimalPlaces 小数位数
 * @return {number}
 * @author: wiz
 * @Date: 2023-08-11 14:59:38
 * @LastEditors: wiz
 * @LastEditTime: Do not edit
 */
function toFixedDecimal(d: number, decimalPlaces: number): number {
  // 将数字转换为字符串
  const numberString = d.toString();

  // 查找小数点的位置
  const decimalIndex = numberString.indexOf('.');

  // 如果没有小数点，则直接返回整数部分的字符串
  if (decimalIndex === -1) {
    return d;
  }

  // 获取小数部分的长度
  const actualDecimalLength = numberString.length - decimalIndex - 1;

  // 计算需要保留的小数位数
  const targetDecimalPlaces = Math.min(actualDecimalLength, decimalPlaces);

  // 返回指定小数位数的字符串表示
  return parseFloat(
    numberString.slice(0, decimalIndex + targetDecimalPlaces + 1)
  );
}
/**
 * @description: 压缩字符串
 * @param {string} s
 * @return {string}
 * @author: wiz
 * @Date: 2023-07-14 12:32:12
 * @LastEditors: wiz
 * @LastEditTime: Do not edit
 */
function compressString(s: string): string {
  return JSON.stringify(Array.from(pako.gzip(s, { to: 'string' })));
}
/**
 * @description: 解压缩字符串
 * @param {string} s
 * @return {string}
 * @author: wiz
 * @Date: 2023-07-14 12:32:12
 * @LastEditors: wiz
 * @LastEditTime: Do not edit
 */
function decompressString(s: string): string {
  return pako.ungzip(new Uint8Array(JSON.parse(s)), { to: 'string' });
}

/**
 * @description: 获得html元素到屏幕的上下左右的距离
 * @param {HTMLElement} element
 * @return {{left,top,right,bottom:number}}
 * @author: wiz
 * @Date: 2023-07-14 12:32:12
 * @LastEditors: wiz
 * @LastEditTime: Do not edit
 */
function getElementDistanceFromScreenEdges(element: HTMLElement) {
  const rect = element.getBoundingClientRect();
  const scrollX =
    window.scrollX || window.pageXOffset || document.documentElement.scrollLeft;
  const scrollY =
    window.scrollY || window.pageYOffset || document.documentElement.scrollTop;

  // 元素左上角到屏幕左边缘的距离
  const left = rect.left + scrollX;

  // 元素左上角到屏幕上边缘的距离
  const top = rect.top + scrollY;

  // 元素右下角到屏幕右边缘的距离（注意：是距离，不是坐标）
  const right = window.innerWidth - (left + rect.width);

  // 元素右下角到屏幕下边缘的距离（注意：是距离，不是坐标）
  const bottom = window.innerHeight - (top + rect.height);

  return {
    left,
    top,
    right,
    bottom
  };
}
/**
 * @description: 获得中国格式的当前时间字符串 2023-04-05 14:30:00
 * @param {}
 * @return {string} 2023-04-05 14:30:00
 * @author: wiz
 * @Date: 2023-07-14 12:32:12
 * @LastEditors: wiz
 * @LastEditTime: Do not edit
 */
function getCurrentTimeStrCN(): string {
  const now = new Date(); // 获取当前时间
  const year = now.getFullYear(); // 年
  const month = String(now.getMonth() + 1).padStart(2, '0'); // 月（+1因为月份是从0开始的）
  const day = String(now.getDate()).padStart(2, '0'); // 日
  const hours = String(now.getHours()).padStart(2, '0'); // 小时
  const minutes = String(now.getMinutes()).padStart(2, '0'); // 分钟
  const seconds = String(now.getSeconds()).padStart(2, '0'); // 秒

  // 将它们组合成一个字符串
  return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
}
/**
 * @description: 计算文件SHA-256指纹
 * @param {File}file
 * @return {Promise<string>}
 * @author: wiz
 * @Date: 2023-07-14 12:32:12
 * @LastEditors: wiz
 * @LastEditTime: Do not edit
 */
async function calculateFileSHA256(file: File): Promise<I_return> {
  const buffer = await file.arrayBuffer();
  const data = new Uint8Array(buffer);
  const hashHex = wCalculateSHA256(data);
  return hashHex;
}

export default {
  /**
   * @description: 将传入对象转换为数据
   *               1、排除对象属性名为_XID的属性
   *               2、排除对象属性名包含| 和 /
   * @param {object} obj 传入对象
   * @return {Array<{name:string ; value:any}>}
   * @author: wiz
   * @Date: 2023-07-14 12:32:12
   * @LastEditors: wiz
   * @LastEditTime: Do not edit
   */
  getObjNameValueToArr, // 转换对象为 对象名+对象值数组
  /**
   * @description: 传入obj转换为数据
   * @return {*}
   * @author: wiz
   * @Date: 2023-07-14 21:09:50
   * @LastEditors: clr
   * @LastEditTime: Do not edit
   */
  objToArr,
  /**
   * @description: 将传入的数据利用base64编码加密成新的字符串//明文加密
   * @param {string} str
   * @return {*}
   * @author: wiz
   * @Date: 2023-07-14 21:56:05
   * @LastEditors: clr
   * @LastEditTime: Do not edit
   */
  EnCode,
  // 明文解密
  /**
   * @description: 将传入数据通过base64解码
   * @param {string} str
   * @return {*}
   * @author: wiz
   * @Date: 2023-07-14 22:18:41
   * @LastEditors: clr
   * @LastEditTime: Do not edit
   */
  DeCode,
  /**
   * @Author: wiz
   * @param {boolean} delimiter 是否要分隔符 默认true
   * @return {*}
   * @description: guid
   */
  getGUID,
  /**
   * @description: 切分m2o的字符串
   *               传入值必须是包含3个'|'
   * @param {string} field - 字符串
   * @return {{ type: string; field: string; toTable: string; toField: string }|undefined}
   * @author: clr
   * @Date: 2023-07-02 16:02:06
   * @LastEditors: clr
   * @LastEditTime: Do not edit
   */

  splitM2OField,
  /**
   * @description:传入字符串或数组类型，并根据val的类型返回不同的配置对象
   * @param {string} 根据 val中的 ：切分传出toTable: m2o[0],toField: m2o[1],fields: [],isDropdown: false
   * @param {object} 如果传入的是object，传出val
   * @return {*}
   * @author: wiz
   * @Date: 2023-07-13 20:48:27
   * @LastEditors: clr
   * @LastEditTime: Do not edit
   */
  getM2OConfig,
  /**
   * @description: 遇到 ‘|’切分成，返回type，field，selectionID
   *               如果传入值的切分值长度小于3组，返回NUll。
   * @param {string} field
   * @return {object}
   * @author: wiz
   * @Date: 2023-07-13 20:52:38
   * @LastEditors: clr
   * @LastEditTime: Do not edit
   */
  splitSelectionField,
  /**
   * @description: findComponent转换组件名‘含-变成oname,不含-的就成o-name
   * @param {string} name
   * @return {*}
   * @author: wiz
   * @Date: 2023-07-13 21:10:39
   * @LastEditors: clr
   * @LastEditTime: Do not edit
   */
  findComponent,
  // 横线转驼峰写法
  /**
   * @description: 横线转驼峰写法
   * @param {string} name
   * @return {*}
   * @author: wiz
   * @Date: 2023-07-14 22:31:22
   * @LastEditors: clr
   * @LastEditTime: Do not edit
   */
  toHump,
  /**
   * @description: 驼峰转换横线
   * @param {string} name
   * @return {*}
   * @author: wiz
   * @Date: 2023-07-14 22:34:32
   * @LastEditors: clr
   * @LastEditTime: Do not edit
   */
  toLine,
  /**
   * @description: 判断传入的值是否为 null|undefined，""|"   "，[]，
   * @param {any} val
   * @return {boolean}
   * @author: clr
   * @Date: 2023-07-02 16:02:06
   * @LastEditors: clr
   * @LastEditTime: Do not edit
   */
  noValue,
  /**
   * @description: 判断传入的值是否为 string
   * @param {string} str
   * @return {boolean}
   * @author: clr
   * @Date: 2023-07-02 16:02:06
   * @LastEditors: clr
   * @LastEditTime: Do not edit
   */
  isJSONString,
  /**
   * @description: 链接table和field
   * @param {string} table 表名
   * @param {string} field 字段名
   * @return {*}
   * @author: wiz
   * @Date: 2023-07-13 21:09:10
   * @LastEditors: clr
   * @LastEditTime: Do not edit
   */
  joinTF,
  configRecurse,
  configRecurseSingle,
  getM2OConfigOption,
  /**
   * @description: 获取文件类型
   * @param {string} fileSrc 文件名
   * @return {*}
   * @author: wiz
   * @Date: 2023-07-19 22:12:44
   * @LastEditors: clr
   * @LastEditTime: Do not edit
   */
  getFileType,
  /**
   * @description: 获取文件名包含扩展名
   * @param {string} fileSrc 文件名
   * @return {*}
   * @author: wiz
   * @Date: 2023-07-19 22:11:52
   * @LastEditors: clr
   * @LastEditTime: Do not edit
   */
  getFileName,
  /**
   * @description: 获取文件扩展名
   * @param {string} fileSrc 文件名
   * @return {*}
   * @author: wiz
   * @Date: 2023-07-19 22:10:53
   * @LastEditors: clr
   * @LastEditTime: Do not edit
   */
  getFileExt,
  /**
   * @description:将传入的dataual值转换为blob对象
   * @param {string} dataurl
   * @return {*}
   * @author: wiz
   * @Date: 2023-07-19 22:15:37
   * @LastEditors: clr
   * @LastEditTime: Do not edit
   */
  dataURLtoBlob,
  /**
   * @description: 下载文件
   * @param {string} url
   * @param {string} name
   * @return {*}
   * @author: wiz
   * @Date: 2023-07-19 22:19:40
   * @LastEditors: clr
   * @LastEditTime: Do not edit
   */
  downloadFile,
  /**
   * @description: 下载文件（按Base64）
   * @param {string} base64
   * @param {string} name
   * @return {*}
   * @author: wiz
   * @Date: 2023-07-19 22:20:40
   * @LastEditors: clr
   * @LastEditTime: Do not edit
   */
  downloadFileByBase64,
  /**
   * @description: 获取文件的路径
   * @param {string} id  文件id
   * @param {boolean} isThumb 是否缩略图
   * @return {*}
   * @author: wiz
   * @Date: 2023-07-19 22:13:32
   * @LastEditors: clr
   * @LastEditTime: Do not edit
   */
  // getFileSrc,
  /**
   * @description: 用于创建一个弹窗（模态框）并返回一个Promise对象
   * @param {string} name 弹窗名
   * @param {any} config  弹窗配置
   * @return {*}
   * @author: wiz
   * @Date: 2023-07-22 12:19:34
   * @LastEditors: clr
   * @LastEditTime: Do not edit
   */
  createModal,
  /**
   * @description: 判断是否包含中文
   * @param {*} str
   * @return {*}
   * @author: wiz
   * @Date: 2023-07-19 22:09:09
   * @LastEditors: clr
   * @LastEditTime: Do not edit
   */
  isContainChinese,
  // getFileInfoById,
  // getTime,
  valueToFilter,
  /**
   * @description: 获取列类型
   * @param {ColumnConfig} column
   * @return {*}
   * @author: wiz
   * @Date: 2023-07-19 22:23:14
   * @LastEditors: clr
   * @LastEditTime: Do not edit
   */
  getColumnType,
  /**
   * @description: 获取url参数名称
   * @param {string} name
   * @return {*}
   * @author: wiz
   * @Date: 2023-07-19 22:26:45
   * @LastEditors: clr
   * @LastEditTime: Do not edit
   */
  getURLParam,
  /**
   * @description: 传入日期和模板,得到当年周数
   * @param {any} date 日期
   * @param {number} mode 模式3|6
   * * mode 6 周日为星期一, mode 3 周 1 为星期一
   * 参考mysql YEARWEEK('2016-12-31',3)模板用法与数据库一致
   * mode只接受 3|6 如果不是3或6 则按3处理
   * @return {*}
   * @author: wiz
   * @Date: 2023-07-19 22:33:31
   * @LastEditors: clr
   * @LastEditTime: Do not edit
   */
  GetCurrWeekByDate,
  /**
   * @description: 获取一年最后一周的天数
   * @param year 四位数字年
   * @param mode  模式 3|6
   * mode 6 周日为星期一, mode 3 周 1 为星期一
   * 参考mysql YEARWEEK('2016-12-31',3)模板用法与数据库一致
   * mode只接受 3|6 如果不是3或6 则按3处理
   * @returns 天数
   */
  GetYearLastWeekDays,
  /**
   * @description: 获取一年第一周的天数
   * @param {number} year 四位数字年
   * @param {number} mode 模式3|6
   * * mode 6 周日为星期一, mode 3 周 1 为星期一
   * 参考mysql YEARWEEK('2016-12-31',3)模板用法与数据库一致
   * mode只接受 3|6 如果不是3或6 则按3处理
   * @return {*} 天数
   * @author: wiz
   * @Date: 2023-07-19 22:29:36
   * @LastEditors: clr
   * @LastEditTime: Do not edit
   */
  GetYearFirstWeekDays,
  /**
   * @description:得到一年总周数
   * @param year 四位数字年
   * @param mode  模式 3|6
   * mode 6 周日为星期一, mode 3 周 1 为星期一
   * 参考mysql YEARWEEK('2016-12-31',3)模板用法与数据库一致
   * mode只接受 3|6 如果不是3或6 则按3处理
   * @returns 周数
   */
  GetYearSumWeeks,
  /**
   * @Author: wiz
   * @param treeArr 树结构数组
   * callBack(item)处理回调 例如映射地图
   * keyName key名默认 id
   * children 下一层名 默认 children
   * parentName 父ID属性名 默认 parentID
   * @return {Array<any>}
   * @description:在树结构数组中加入id 和 parentID
   * 如果对象带keyName 则不处理,但callBack会执行
   */
  addTreeKeyValue,
  /**
   * @Author: wiz
   * @param {*} source
   * @param {*} target
   * @return {*}
   * @description: 将对象source的相同属性值复制到target
   */
  listAssign,
  /**
   * @Author: wiz
   * @param {*} obj
   * @return {*}
   * @description: 所有属性为null
   */
  objAttrClear,
  /**
   * @description: 根据给定日期获取一周的起始日期的更新版
   * @param {Date} P_date
   * @param {number} P_mode
   * @return {*}
   * @author: wiz
   * @Date: 2023-07-19 22:39:10
   * @LastEditors: clr
   * @LastEditTime: Do not edit
   */
  GetCurrWeekByDateNew,
  /**
   * @description: 根据给定日期获取一周的起始日期
   * @param {Date} P_date
   * @param {number} P_mode
   * @return {*}
   * @author: wiz
   * @Date: 2023-07-19 22:39:10
   * @LastEditors: clr
   * @LastEditTime: Do not edit
   */
  getWeekStartByDate,
  /**
   * @description: 产生UUID
   * @param {number} len 自定义长度UUID 默认0 标准UUID长度
   * @param {number} radix 进制 2 10 16 参数无效产生标准UUID
   * @param {string} range  'letters'(大小写字母)  | 'number'(数字) | 'lettersANDnumber' (字母+数字)
   *                        默认 'lettersANDnumber' (字母+数字)
   * @return {string} 返回字符串
   * @author: wiz
   * @Date: 2023-07-06 12:43:04
   * @LastEditors: wiz
   * @LastEditTime: Do not edit
   */
  custUUID,
  /**
   * @description:将传入的Map转为数据
   * @param {Map<string, any>} m
   * @param {*} any
   * @return {*} Array<obj> | []
   * @author: wiz
   * @Date: 2023-07-14 20:46:45
   * @LastEditors: clr
   * @LastEditTime: Do not edit
   */
  mapToArr,
  /**
   * @description: 定时执行
   * @param {number} intervalTime 间隔时间(毫秒)
   * @param {Function} callback  要执行的回调()=>boolean
   * @param {number} totalDuration  总时长 毫秒如果是 此参数必须是intervalTime的倍数
   * @param {boolean} interrupted  在总时长内只要执行一次成功就中断
   * @return {Promise<any>}
   * @author: wiz
   * @Date: 2023-07-06 12:43:04
   * @LastEditors: wiz
   * @LastEditTime: Do not edit
   */
  timedExec,

  /**
   * @description: 克隆不包括vue,vue对象只是引用关系
   *               对Set类型也有效
   * @param {any} o 对象
   * @param {boolean} deep 深复制 默认false
   * @return {any} 返回复制后的对象
   * @author: wiz
   * @Date: 2023-07-06 12:43:04
   * @LastEditors: wiz
   * @LastEditTime: Do not edit
   */
  cloneNoMatchVue,
  /**
   * @description: 通过路径查找获取值
   * @param {any} obj
   * @param {string} path 'a.b.c'
   * @return {*} 未找到返回undefined
   * @author: wiz
   * @Date: 2023-07-06 12:43:04
   * @LastEditors: wiz
   * @LastEditTime: Do not edit
   */
  getObjValByPath,
  /**
   * @description: 通过路径设置对象值
   * @param {any} obj
   * @param {string} path
   * @param {any} value
   * @return {*}
   * @author: wiz
   * @Date: 2023-07-06 12:50:17
   * @LastEditors: wiz
   * @LastEditTime: Do not edit
   */
  setObjValByPath,
  /**
   * @description: 通过值获取对象的路径
   * @param {any}obj 对象
   * @param {any}targetValue 目标值
   * @param {any}currentPath 当前路径默认''
   * @return {string | undefined}
   * @author: wiz
   * @Date: 2023-07-15 13:11:47
   * @LastEditors: wiz
   * @LastEditTime: Do not edit
   */
  findPaths,
  /**
   * @description: 通过值获取对象的路径
   *               返回第一次符合的路径
   * @param {any}obj 对象
   * @param {any}targetValue 目标值
   * @param {any}currentPath 当前路径默认''
   * @return {string | undefined}
   * @author: wiz
   * @Date: 2023-07-15 13:11:47
   * @LastEditors: wiz
   * @LastEditTime: Do not edit
   */
  findPathFirst,
  /* Crypto-js是一个JavaScript库，用于提供各种加密和解密算法的实现。
  它支持常见的加密算法，
  如AES、DES、Triple DES、Rabbit、RC4、MD5、SHA-1、SHA-256等。 */
  CryptoJS,
  /**
   * @description:获得md5哈希值
   * @param {string} p
   * @return {*} md5哈希值
   * @author: wiz
   * @Date: 2023-08-11 14:49:11
   * @LastEditors: wiz
   * @LastEditTime: Do not edit
   */
  md5,
  /**
   * @description: AES加密
   * @param {string} data 数据
   * @param {string} key  盐
   * @return {string}
   * @author: wiz
   * @Date: 2023-08-11 14:57:46
   * @LastEditors: wiz
   * @LastEditTime: Do not edit
   */
  aesEncrypt,
  /**
   * @description: ASE解密
   * @param {string} data 数据
   * @param {string} key 盐
   * @return {*}
   * @author: wiz
   * @Date: 2023-08-11 14:59:38
   * @LastEditors: wiz
   * @LastEditTime: Do not edit
   */
  aesDecrypt,
  /**
   * @description: DES加密
   * @param {string} data 数据
   * @param {string} key  盐
   * @return {string}
   * @author: wiz
   * @Date: 2023-08-11 14:57:46
   * @LastEditors: wiz
   * @LastEditTime: Do not edit
   */
  desEncrypt,
  /**
   * @description: DES解密
   * @param {string} data 数据
   * @param {string} key 盐
   * @return {*}
   * @author: wiz
   * @Date: 2023-08-11 14:59:38
   * @LastEditors: wiz
   * @LastEditTime: Do not edit
   */
  desDecrypt,
  /**
   * @description: 返回串中的中文个数，字母和数据的个数
   * @param {string} str
   * @return {*}
   * @author: wiz
   * @Date: 2023-08-22 19:24:36
   * @LastEditors: wiz
   * @LastEditTime: Do not edit
   */
  countCharacters,

  /**
   * @description: 比较两个对象的属性是否相等
   * @param {string} obj1
   * @param {string} obj2
   * @param {number} depth 比较的深度层次控制，默认为0
   *                       如果 depth 为 <=0，则不再进行深度比较
   *                       如果传入带小数则取整数位处理
   * @return {boolean} true 相同 false 不同
   * @author: wiz
   * @Date: 2023-08-22 19:24:36
   * @LastEditors: wiz
   * @LastEditTime: Do not edit
   */
  compareObjectProperties,

  /**
   * @description: 定义小数的长度
   * @param {number} d 数据
   * @param {number} decimalPlaces 小数位数
   * @return {number}
   * @author: wiz
   * @Date: 2023-08-11 14:59:38
   * @LastEditors: wiz
   * @LastEditTime: Do not edit
   */
  toFixedDecimal,
  /**
   * @description: 压缩字符串
   * @param {string} s
   * @return {string}
   * @author: wiz
   * @Date: 2023-07-14 12:32:12
   * @LastEditors: wiz
   * @LastEditTime: Do not edit
   */
  compressString,
  /**
   * @description: 解压缩字符串
   * @param {string} s
   * @return {string}
   * @author: wiz
   * @Date: 2023-07-14 12:32:12
   * @LastEditors: wiz
   * @LastEditTime: Do not edit
   */
  decompressString,

  /**
   * @description: 获得html元素到屏幕的上下左右的距离
   * @param {HTMLElement} element
   * @return {{left,top,right,bottom:number}}
   * @author: wiz
   * @Date: 2023-07-14 12:32:12
   * @LastEditors: wiz
   * @LastEditTime: Do not edit
   */
  getElementDistanceFromScreenEdges,

  /**
   * @description: 获得中国格式的当前时间字符串 2023-04-05 14:30:00
   * @param {}
   * @return {string} 2023-04-05 14:30:00
   * @author: wiz
   * @Date: 2023-07-14 12:32:12
   * @LastEditors: wiz
   * @LastEditTime: Do not edit
   */
  getCurrentTimeStrCN,

  /**
   * @description: 计算文件SHA-256指纹
   * @param {File}file
   * @return {Promise<I_return>}
   * @author: wiz
   * @Date: 2023-07-14 12:32:12
   * @LastEditors: wiz
   * @LastEditTime: Do not edit
   */
  calculateFileSHA256
};
